#Produce a nine-man plot (described below).
#Player names are used as plot titles. If names are not available in the data frame,
#The function attempts to download them from the MLB servers.
plot.pitchfx.nineman<-function(pitcher,subtype,players=NULL,...) {

  year<-as.character(pitcher$year[1])
  month<-as.character(pitcher$month[1])
  day<-as.character(pitcher$day[1])
  team<-as.character(pitcher$home[1])
  
  if(is.null(subtype)) {
    type = "location"
  } else {
    type = subtype
  }

  p<-pitcher
  batters<-unique(p$batter)

  if(!("name" %in% names(pitcher))) {
    if(is.null(players)) {
      players <- read.pitchfx(makeurl(year,month,day,team=team,mode="players"),mode="getplayers")
      names <- character(0)
      for(i in 1:9) names <- c(names,getname(players,batters[i]))
    }
  } else {
    names <- character(0)
    for(i in 1:9) names <- c(names,p$name[p$batter==batters[i]][1])
  }

  par(mfrow=c(3,3))
  for(i in 1:9) {
    plot.pitchfx(p[p$batter==batters[i],],type=type,title=names[i],...)
  }
  par(mfrow=c(1,1))

}

#Plots of pitch locations for different counts
plot.pitchfx.count<-function(pitcher,subtype,...) {

  if(is.null(subtype)) {
    type = "location"
  } else {
    type = subtype
  }

  p<-pitcher
  p$count<-factor(paste(p$b,p$s,sep="-"))
  counts<-unique(p$count)
  par(mfrow=c(3,4))
  for(i in 1:length(counts)) {
    plot.pitchfx(p[p$count==counts[i],],title=counts[i],type=type,...)
  }
  par(mfrow=c(1,1))

}

#Plot Mike Fast's polar graph of speed and spin deflection
plot.pitchfx.fastpolar <-function(pitcher,title="",col,sym) {
  require(plotrix)

  speed<-pitcher$start_speed
  sp<-spin(pitcher)[,1]
  speed<-speed[which(!is.na(speed))]
  sp<-sp[which(!is.na(sp))]
  polar.plot(speed,sp,rp.type="s",point.symbols=sym,point.col=col,main=title)

}

#Plot a player's hitting zones.

plot.pitchfx.hitzone<-function(pitches,type="panel",add=F,...) {

  zones <- hit.zones(pitches,...)
  zone <- zones[[1]]
  totals <- round(zones[[2]],3)
  brk<-dim(zone)[1]
  buffer<-zones[[3]]
  offsets<-zones[[4]]

  image.dim<-c(0,0,1,1)
  xorder<-brk:1
  yorder<-1:brk
  if(add) {
    image.dim<-c(-1,mean(pitches$sz_bot,na.rm=T),
                  1,mean(pitches$sz_top,na.rm=T))+buffer[c(4,3,2,1)]*c(-1,-1,1,1)+c(offsets[1],offsets[2],offsets[1],offsets[2])
    yorder<-brk:1
  }

  xrange<-seq(image.dim[1],image.dim[3],(image.dim[3]-image.dim[1])/(brk))
  yrange<-seq(image.dim[2],image.dim[4],(image.dim[4]-image.dim[2])/(brk))
  
  textx<-rep(xrange[1:brk],brk)+(xrange[brk+1]-xrange[1])/(2*brk)
  texty.temp<-yrange[1:brk]+(yrange[brk+1]-yrange[1])/(2*brk)
  texty<-rep(texty.temp[1],brk)
  for(i in 2:length(texty.temp)) {
    texty <- c(texty,rep(texty.temp[i],brk))
  }
  texty2<-texty-((image.dim[4]-image.dim[2])/15)
  
  par(mar=c(2,2,2,1))
  
  if(type=="panel") {
    par(mfrow=c(1,4))
  }
    if(type=="panel" | type=="swing") {
      image(xrange,yrange,
       t(zone[xorder,yorder,1]),col=heat.colors(20)[20:1],zlim=c(0.2,0.9),
       main=paste("Swing: ",totals[1],collapse=""),add=add,
       xaxt="n",yaxt="n",
       xlab="",ylab="")
      text(textx,texty,round(t(zone[xorder,yorder,1]),3))
      text(textx,texty2,t(zone[xorder,yorder,6]))
    }
    if(type=="panel" | type=="contact") {
      image(xrange,yrange,
       t(zone[xorder,yorder,2]),col=heat.colors(20)[20:1],zlim=c(0.5,1),
       main=paste("Contact: ",totals[2],collapse=""),add=add,
       xaxt="n",yaxt="n",
       xlab="",ylab="")
      text(textx,texty,round(t(zone[xorder,yorder,2]),3))
      text(textx,texty2,t(zone[xorder,yorder,5]))
    }
    if(type=="panel" | type=="inplay") {
      image(xrange,yrange,t(zone[xorder,yorder,3]),col=heat.colors(20)[20:1],
      zlim=c(0.1,0.8),
      main=paste("In play: ",totals[3],collapse=""),add=add,
      xaxt="n",yaxt="n",
       xlab="",ylab="")
      text(textx,texty,round(t(zone[xorder,yorder,3]),3))
      text(textx,texty2,t(zone[xorder,yorder,5]))
    }
    if(type=="panel" | type=="hit") {
      image(xrange,yrange,t(zone[xorder,yorder,4]),col=heat.colors(20)[20:1],
      zlim=c(0,0.4),
      main=paste("Hit: ",totals[4],collapse=""),add=add,
      xaxt="n",yaxt="n",
       xlab="",ylab="")
      text(textx,texty,round(t(zone[xorder,yorder,4]),3))
      text(textx,texty2,t(zone[xorder,yorder,5]))
    }
  par(mfrow=c(1,1))
  
}

#Add estimated strike zone to an existing plot. Uses modeled strike probabilites
#from strikeprob() to determine the boundaries of the strike zone that was 
#actually called for a player. The "elliptical" type calculates a round strike
#zone, while the "rectangular" type calculates a rectangular zone based on the
#procedure described by John Walsh:
#http://www.hardballtimes.com/main/article/strike-zone-fact-vs-fiction/
#
#the 'boundary' parameter specifies the strike probability level where the line
#is drawn. 'sensitivity' and 'step' can be used to fine-tune the appearance of the
#strike zone line for the elliptical strike zone. 'cutoff' controls tightly the
#pitches are selected when calculating vertical and horizontal strike zones in
#rectangular mode. 

draw.truezone <- function(pitches,boundary=0.5,sensitivity=0.02,step=0.05,
			  type="elliptical",cutoff=0.5) {
  if(type=="elliptical") {
    b.zone<-strikeprob(pitches,return.model=T)
    coefs<-coef(b.zone)
    strike <- function(x,y) invlogit(coefs[1] + coefs[2]*x + coefs[3]*I(x^2)
					      + coefs[4]*y + coefs[5]*I(y^2))
    xrange <- c(-2,2)
    yrange <- c(1,4)
    x <- seq(xrange[1],xrange[2],step)
    y <- seq(yrange[1],yrange[2],step)

    zone <- matrix(NA,length(x),length(y))
    draw <- c(NA,NA)
    for(i in 1:length(x)) {
      for(j in 1:length(y)) {
	zone[i,j] <- strike(x[i],y[j])
	if(zone[i,j] < (boundary+sensitivity) & zone[i,j] > (boundary - sensitivity)) draw <- rbind(draw,c(x[i],y[j]))
      }
    }
    draw1 <- draw[1:(nrow(draw)/2),]
    draw2 <- draw[(nrow(draw)/2):(nrow(draw)),]
    lines(draw1[order(draw1[,2]),])
    lines(draw2[order(draw2[,2]),])
  }
  if(type=="rectangular") {
    m1 <- bayesglm(calledstrike~px+I(px^2),
               data=pitches[abs(mean(pitches$pz,na.rm=T)-pitches$pz)<cutoff*sd(pitches$pz,na.rm=T),],
	       family=binomial(link="logit"))
    m2 <- bayesglm(calledstrike~pz+I(pz^2),
               data=pitches[abs(mean(pitches$px,na.rm=T)-pitches$px)<cutoff*sd(pitches$px,na.rm=T),],
	       family=binomial(link="logit"))
    xbounds <- solve(polynomial(coef(m1)) - logit(boundary))
    ybounds <- solve(polynomial(coef(m2)) - logit(boundary))
    lines(xbounds,rep(ybounds[1],2))
    lines(xbounds,rep(ybounds[2],2))
    lines(rep(xbounds[1],2),ybounds)
    lines(rep(xbounds[2],2),ybounds)
  }
}

#Plot pitches from a pitchfx object. Possible types of graph are:
#
# location:   location where pitch crosses the plate. The default.
# break:      horizontal and vertical movement
# spin:       Speed vs. spin plot, using Fast & Nathan's equation
# pitchspeed: pitch-by-pitch plot of velocity
# release:    release point
# nineman:    a 3x3 set of graphs showing a location plot for the first nine 
#             unique batters in the dataset. Most useful for studying starting 
#             pitchers. This will produce weird results if you pass it something 
#             other than a single game, one-pitcher dataset.
# count:      a 3x4 set of graphs showing a location plot for each count. Most 
#             useful for studying a single pitcher or batter.
# fast:       a polar plot of speed and spin direction, based on Mike Fast's 
#             work. Right now it ignores all of the parameters except "title", 
#             and just gives a raw plot of the pitches in the input dataset.
# selectivity:A selectivity curve showing the batter's probability of swinging
#             relative to the probability the pitch is a strike. Works best when
#             the input data contains a large number of pitches for a single
#             batter.
# trajectory: An interpolation of the path of the ball from the release point to
#             the plate, based on the initial position, velocity and
#             acceleration information. The trajectory can be viewed from
#             different perspectives using the "pov" option. The perspectives
#             include the point of view of right and left handed batters, based
#             on Mike Fast's co-ordinate transformation method. With this method,
#             You should use data from a single pitcher and pitch type.
# hit:        Hit location for balls in play. Coordinates are transformed to
#             approximate feet, but this is inexact because the original scale
#             of measurement is unknown.
# hitzone:    Player's hitting zone. Can be used to show swing, contact,
#             in-play and hit rates for different parts of the strike zone.
#  
#You can classify pitches according to any variable available in the input 
#dataset. Pitches can be simultaneously classified in two ways, using the 
#"classify" parameter to color code and the "symbolize" parameter for different 
#symbols. You can also scale pitches according to velocity. Some of the plotting
#methods will ignore the classification options, however.


plot.pitchfx <- function(
  pitcher,
  title="",
  classify="type",
  colorset=c("black","green","red","blue","gray","orange","pink","cyan",
			 "yellow","darkgreen","darkred","darkblue","darkorange","purple",
			 "wheat","plum","olivedrab","palegreen"),
  color.categories=NULL, 
  color.legend.pos="topright",
  symbolize="",
  symbolset=c(16,1:15),
  symbol.categories=NULL,
  symbol.legend.pos="topleft",
  pointsize=1, 
  size.prop=FALSE,
  legend=TRUE,
  type="location",
  textsize=1,
  lwd=1,
  players=NULL,
  add=FALSE,
  strikes=3,
  pov="umpire",
  xlim=NULL,
  ylim=NULL,
  subset=NULL,
  subtype=NULL,
  strikezone="rulebook",
  boundary=0.5,
  sensitivity=0.02,
  step=0.05,
  cutoff=0.5,
  file = NULL,
  dim = c(400,400),
  ...) {

  if(!is.null(file)) png(paste(file,".png",sep=""),height=dim[1],width=dim[2])

  done = FALSE

  if(title=="" & "name" %in% names(pitcher)) if(length(unique(pitcher$name))==1) title <- pitcher$name[1]

  if(!is.null(subset)) {
    pitcher<-pitcher[which(pitcher[[subset[[1]]]] %in% subset[[2]]),]
  }

  numplots<-1
  draw.strikezone<-FALSE
  draw.field<-FALSE
  zone<-c(mean(pitcher$sz_top,na.rm=T),mean(pitcher$sz_bot,na.rm=T))
  sz<-rbind(c(-1,1),c(zone[2],zone[2]),c(-1,-1),c(zone[2],zone[1]),c(-1,1),c(zone[1],zone[1]),c(1,1),c(zone[1],zone[2]))
  xaxt="s"
  yaxt="s"
  
  if(type=="location") {
    draw.strikezone<-strikezone
  }

  if(type=="nineman") {
    plot.pitchfx.nineman(pitcher,classify=classify,symbolize=symbolize,legend=TRUE,
		colorset=c("black","green","red","blue","orange","pink","cyan","yellow","gray","darkgreen"),  
		color.categories=NULL,
		symbol.categories=NULL,  
		pointsize=1,
		textsize=1,
		lwd=1,
		players=players,
		subtype=subtype)
    done = TRUE
  }

  if(type=="count") {
    plot.pitchfx.count(pitcher,subtype=subtype,classify=classify,symbolize=symbolize,
	  legend=TRUE,colorset=c("black","green","red","blue","orange",
	  "pink","cyan","yellow","gray","darkgreen"),  color.categories=NULL,
	  symbol.categories=NULL,  
	  pointsize=1,
	  textsize=1,
	  lwd=1)
    done = TRUE
  }
 
 if(type=="hitzone") {
  subtype<-ifelse(is.null(subtype),ifelse(add,"hit","panel"),subtype)
  plot.pitchfx.hitzone(pitcher,
    type=subtype,
    add=add,
    ...)
  done = TRUE
 }
  
  if(!done) {
  sym<-rep(16,nrow(pitcher))
  if(length(symbolize)==nrow(pitcher)) {
    forsym <- symbolize
  } else if(symbolize[1]=="") {
    forsym <- rep(1,nrow(pitcher))
  } else {
    forsym <- pitcher[[symbolize]]
  }

  if(is.null(symbol.categories)) {
		symbols<-sort(unique(forsym))
     } else {
		symbols<-symbol.categories
		}
  for(i in 1:nrow(pitcher)) {
    if(!is.na(forsym[i])) {
		sym[i]<-symbolset[which(symbols==forsym[i])]
	}
    }

  if(length(classify)==nrow(pitcher)) {
      t <- classify
  } else if(classify=="") {
    t <- rep(1,nrow(pitcher))
  } else {
    t<-pitcher[[classify]]
  }

  if(is.null(color.categories)) {
	classes<-sort(unique(t))
    } else {
	classes<-color.categories
	}
  if(NA %in% classes) {
  classes<-classes[-which(is.na(classes))]
  }
  col=rep(1,nrow(pitcher))
  for(i in 1:nrow(pitcher)) {
    if(length(which(classes==t[i]))>0) {
		col[i]<-colorset[which(classes==t[i])]
	} else {
		col[i]<-0
	}
                                          
  }

 }

  if(type=="fast") {
    plot.pitchfx.fastpolar(pitcher,title=title,col=col,sym=sym)
    done = TRUE
  }

 if(!done) {
  levels(pitcher$pitch_type)<-c(levels(pitcher$pitch_type),"?")
  pitcher$pitch_type[is.na(pitcher$pitch_type)]<-"?"
  xlab="horizontal"
  ylab="vertical"    
  tp="p"
  if(type=="location") {
    if(is.null(xlim)) xlim=c(-3,3)
    if(is.null(ylim)) ylim=c(0,5)
    x<-pitcher$px
    y<-pitcher$pz
  }
  if(type=="break") {
    x<-pitcher$pfx_x
    y<-pitcher$pfx_z
    if(is.null(xlim)) xlim=c(min(x,na.rm=T)-2,max(x,na.rm=T)+2)
    if(is.null(ylim)) ylim=c(-15,15)
  }
  if(type=="spin") {
    y<-as.numeric(as.character(pitcher$start_speed))
    x<-spin(pitcher)[,1]
    if(is.null(xlim)) xlim=c(min(spin(pitcher)[,1],na.rm=T)-10,max(spin(pitcher)[,1],na.rm=T)+10)
    if(is.null(ylim)) ylim=c(min(y,na.rm=T)-5,max(y,na.rm=T)+5)
    ylab="speed"
    xlab="spin direction"
  }
  if(type=="pitchspeed") {
    x<-1:dim(pitcher)[1]
    y<-pitcher$end_speed
    if(is.null(xlim)) xlim=c(min(x),max(x))
    if(is.null(ylim)) ylim=c(50,100)
    xlab="pitch"
    ylab="speed"   
    tp="l"
    legend<-FALSE
  }
  if(type=="release") {
    x<-pitcher$x0
    y<-pitcher$z0
    if(is.null(xlim)) xlim<-c(-4,4)
    if(is.null(ylim)) ylim<-c(0,8)
  }
  if(type=="hit") {
    expand<-2.2
    x<-expand*(pitcher$hit_x-125)
    y<-expand*(210-pitcher$hit_y)
    if(is.null(xlim)) xlim=expand*c(-125,125)
    if(is.null(ylim)) ylim=expand*c(-20,230)
    draw.field<-TRUE
  }
  if(type=="selectivity") {
    sel.pitcher<-selectivity.curve(pitcher)
    strike1<-ifelse(strikes==1,1,0)
    strike2<-ifelse(strikes==2,1,0)
    x<-seq(0.01,0.99,.01)
    strike1<-rep(strike1,length(x))
    strike2<-rep(strike2,length(x))    
    y<-invlogit(coef(sel.pitcher)[1]+coef(sel.pitcher)[2]*x+coef(sel.pitcher)[3]*strike1+coef(sel.pitcher)[4]*strike2)
    if(strikes==3) {
      numplots<-3
      y<-invlogit(coef(sel.pitcher)[1]+coef(sel.pitcher)[2]*x)
      extraplots<-matrix(NA,2,length(y))
      extraplots[1,]<-invlogit(coef(sel.pitcher)[1]+coef(sel.pitcher)[2]*x+coef(sel.pitcher)[3])
      extraplots[2,]<-invlogit(coef(sel.pitcher)[1]+coef(sel.pitcher)[2]*x+coef(sel.pitcher)[4])
    }
    xlim<-c(0,1)
    ylim<-c(0,1)
    xlab<-"Strike probability"
    ylab<-"Swing probability"
    tp<-"l"
    legend<-FALSE
    symbolize<-""
    classify=""
    col <- colorset <- colorset[1]
  }

    
  if(type=="trajectory") {
    xlim<-c(-2.5,2.5)
    ylim<-c(0,7)  
    tp="p"
    track<-pitch.trajectory(pitcher)
    if(pov=="umpire") {
      draw.strikezone<-TRUE
    }
    if(pov=="RHB") {
      track<-shift(track$x,track$y,track$z,x0=mean(pitcher$x0,na.rm=T),offset=2.8)
      xlim<-xlim+1.5    
      sz.temp<-cbind(c(sz[1,1],sz[1,2],sz[3,1],sz[3,2],sz[5,1],sz[5,2],sz[7,1],sz[7,2]),c(sz[2,1],sz[2,2],sz[4,1],sz[4,2],sz[6,1],sz[6,2],sz[8,1],sz[8,2]))
      sz.temp<-shift(sz.temp[,1],1.5,sz.temp[,2],x0=mean(pitcher$x0,na.rm=T),offset=2.8)  
      sz[1,]<-sz.temp$x[1:2]
      sz[2,]<-sz.temp$z[1:2]
      sz[3,]<-sz.temp$x[3:4]
      sz[4,]<-sz.temp$z[3:4]
      sz[5,]<-sz.temp$x[5:6]
      sz[6,]<-sz.temp$z[5:6]
      sz[7,]<-sz.temp$x[7:8]
      sz[8,]<-sz.temp$z[7:8]
      draw.strikezone<-FALSE
    }
    if(pov=="LHB") {
      track<-shift(track$x,track$y,track$z,x0=mean(pitcher$x0,na.rm=T),offset=-2.8)
      xlim<-xlim-1.5
       sz.temp<-cbind(c(sz[1,1],sz[1,2],sz[3,1],sz[3,2],sz[5,1],sz[5,2],sz[7,1],sz[7,2]),c(sz[2,1],sz[2,2],sz[4,1],sz[4,2],sz[6,1],sz[6,2],sz[8,1],sz[8,2]))
      sz.temp<-shift(sz.temp[,1],1.5,sz.temp[,2],x0=mean(pitcher$x0,na.rm=T),offset=-2.8) 
      sz[1,]<-sz.temp$x[1:2]
      sz[2,]<-sz.temp$z[1:2]
      sz[3,]<-sz.temp$x[3:4]
      sz[4,]<-sz.temp$z[3:4]
      sz[5,]<-sz.temp$x[5:6]
      sz[6,]<-sz.temp$z[5:6]
      sz[7,]<-sz.temp$x[7:8]
      sz[8,]<-sz.temp$z[7:8]
      draw.strikezone<-FALSE
    }
    x<-track$x
    y<-track$z
    if(pov=="3B") {
      x<-track$y
      y<-track$z
      xlim<-c(55,0)
      tp="b"
    }
    if(pov=="above") {
      x<-track$y
      y<-track$x
      xlim<-c(55,0)
      ylim<-c(-2.5,2.5)
      tp="b"
    }
    xlab=""
    ylab=""
    symbolize<-""
    col<-rep(colorset[1],length(x)) 
    legend<-FALSE
  }
   
  s<-pointsize
  if(size.prop) {
	s<-1.5+0.3*(pitcher$start_speed-mean(pitcher$start_speed,na.rm=T))/sd(pitcher$start_speed,na.rm=T)
	}

  if(!add) {
    par(mar=c(4,4,2,1))
    plot(x,y,pch="",
      xlim=xlim,
      ylim=ylim,
      xlab=xlab,
      ylab=ylab,
      main=title,
      ps=textsize,
      xaxs="i",
      yaxs="i",
      ...)
    }
  if(type=="selectivity") {
    abline(v=0.5,col="gray")
    abline(0.5,0,col="gray")
  }
points(x,y,col=col,pch=sym,cex=s,type=tp,lwd=lwd,xaxt=xaxt,yaxt=yaxt)
  if(numplots>1) for(n in 2:numplots) {
    points(x,extraplots[n-1,],col=col,pch=sym,cex=s,type=tp,lwd=lwd)  
  }
  if(legend & classify[1]!="") {
	legend(color.legend.pos,legend=classes,col=colorset[1:length(classes)],pch=16)
  }
  if(legend & symbolize[1]!="") {
	legend(symbol.legend.pos,legend=symbols,col=1,pch=symbolset[1:length(x)])
  }
  
  if(length(grep("rulebook",draw.strikezone))>0 | draw.strikezone=="all") {
    lines(sz[1,],sz[2,],col="gray")
    lines(sz[3,],sz[4,],col="gray")
    lines(sz[5,],sz[6,],col="gray")
    lines(sz[7,],sz[8,],col="gray")
  }
  if(length(grep("elliptical",draw.strikezone))>0 | draw.strikezone=="all"){
    draw.truezone(pitcher,boundary,sensitivity,step,type="elliptical")
  }
  if(length(grep("rectangular",draw.strikezone))>0 | draw.strikezone=="all") {
    draw.truezone(pitcher,boundary,sensitivity,step,type="rectangular",cutoff=cutoff)
  }
  if(draw.field) {
    lines(expand*c(-30,-150),expand*c(30,150),col="gray")
    lines(expand*c(30,150),expand*c(30,150),col="gray")
    lines(expand*c(0,30,0,-30,0),expand*c(0,30,60,30,0),col="gray")

  }
 }

 if(!is.null(file)) dev.off()

}
